package cn.song.freight.service.impl;

import cn.song.freight.domain.dto.WeightSegmentRuleDTO;
import cn.song.freight.domain.entity.WeightSegmentRuleDO;
import cn.song.freight.domain.result.R;
import cn.song.freight.domain.vo.WeightSegmentRuleVO;
import cn.song.freight.mapper.WeightSegmentRuleMapper;
import cn.song.freight.service.WeightSegmentRuleService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 重量区间规则服务实现类
 * <pre>
 * 该服务实现提供物流系统的重量区间规则管理功能，支持重量区间规则的增删改查操作，
 * 并提供基于运费策略ID的重量区间规则查询功能，用于维护系统中的重量区间定价配置。
 *
 * 核心功能：
 * - 重量区间规则的添加、删除、更新和查询
 * - 支持按运费策略ID查询重量区间规则列表
 * - 数据完整性校验和业务规则验证
 * - 重量区间规则的状态管理和使用情况检查
 *
 * 规则说明：
 * 重量区间规则定义了不同重量段的运费计算方式，通过上限值来区分不同的重量区间
 *
 * @author SongRenShuo
 * @version 3.0.0
 * @since 2025-08-23
 */
@Slf4j
@Service
public class WeightSegmentRuleServiceImpl extends ServiceImpl<WeightSegmentRuleMapper, WeightSegmentRuleDO> implements WeightSegmentRuleService {

    /**
     * 添加重量区间规则
     * <pre>
     * 该方法实现重量区间规则的添加功能，会先校验传入的运费策略ID和重量上限组合是否已存在，
     * 避免重复添加，确保数据的唯一性和一致性，并记录操作时间和操作人信息，便于追踪管理。
     *
     * @param ruleDTO 重量区间规则数据传输对象，包含策略ID、重量上限等信息
     * @return 添加结果，包含添加成功的重量区间规则信息
     */
    @Override
    public R<WeightSegmentRuleDTO> add(WeightSegmentRuleDTO ruleDTO) {
        // V3版本：校验传入的定价配置ID和重量上限组合是否已存在
        LambdaQueryWrapper<WeightSegmentRuleDO> wrapper = Wrappers.<WeightSegmentRuleDO>lambdaQuery()
                .eq(WeightSegmentRuleDO::getPricingId, ruleDTO.getPricingId())
                .eq(WeightSegmentRuleDO::getUpperBound, ruleDTO.getUpperBound());

        if (this.count(wrapper) > 0) {
            return R.failed("该定价配置下的重量上限已存在，请勿重复添加");
        }

        // 构建 DO 对象并填充属性
        WeightSegmentRuleDO entity = new WeightSegmentRuleDO();
        BeanUtils.copyProperties(ruleDTO, entity);
        Date now = new Date();
        entity.setCreateTime(now);
        entity.setUpdateTime(now);

        // 插入数据库
        boolean result = this.save(entity);
        if (!result) {
            return R.failed("重量区间规则添加失败");
        }
        WeightSegmentRuleDTO responseDTO = new WeightSegmentRuleDTO();
        BeanUtils.copyProperties(entity, responseDTO);
        return R.success("添加成功", responseDTO);
    }

    /**
     * 删除重量区间规则
     * <pre>
     * 该方法实现重量区间规则的删除功能，会先校验规则是否存在，
     * 确保删除操作的安全性和准确性，避免误删不存在的数据，并记录操作日志便于追踪。
     *
     * @param id 要删除的重量区间规则ID
     * @return 删除结果
     */
    @Override
    public R<Void> delete(Long id) {
        WeightSegmentRuleDO entity = this.getById(id);
        if (entity == null) {
            return R.failed("重量区间规则不存在");
        }

        boolean result = this.removeById(id);
        return R.isSuccess(result);
    }

    /**
     * 更新重量区间规则
     * <pre>
     * 该方法实现重量区间规则的更新功能，会先校验规则是否存在，
     * 然后根据传入的更新数据对规则进行更新操作，确保数据的实时性和准确性，并记录更新时间和操作人信息。
     *
     * @param ruleDTO 重量区间规则更新数据传输对象，包含要更新的规则ID和新信息
     * @return 更新结果，包含更新成功的重量区间规则信息
     */
    @Override
    public R<WeightSegmentRuleDTO> update(WeightSegmentRuleDTO ruleDTO) {
        WeightSegmentRuleDO entity = this.getById(ruleDTO.getId());
        if (entity == null) {
            return R.failed("重量区间规则不存在");
        }

        // 更新字段
        BeanUtils.copyProperties(ruleDTO, entity);
        entity.setUpdateTime(new Date());

        boolean result = this.updateById(entity);
        if (!result) {
            return R.failed("更新失败");
        }
        WeightSegmentRuleDTO responseDTO = new WeightSegmentRuleDTO();
        BeanUtils.copyProperties(entity, responseDTO);
        return R.success("更新成功", responseDTO);
    }

    /**
     * 根据运费策略ID查询重量区间规则列表
     * <pre>
     * 该方法实现按运费策略ID查询重量区间规则列表的功能，查询结果按重量上限升序排列，
     * 便于运费计算时按重量从小到大依次匹配规则，确保计算结果的准确性。
     *
     * @param strategyId 运费策略ID
     * @return 符合条件的重量区间规则列表
     */
    @Override
    public List<WeightSegmentRuleVO> listByStrategyId(Long strategyId) {
        LambdaQueryWrapper<WeightSegmentRuleDO> wrapper = Wrappers.<WeightSegmentRuleDO>lambdaQuery()
                .eq(WeightSegmentRuleDO::getStrategyId, strategyId);
        // 按定价ID倒序，然后按重量上限升序
        wrapper.orderByDesc(WeightSegmentRuleDO::getPricingId);
        wrapper.orderByAsc(WeightSegmentRuleDO::getUpperBound);

        return getWeightSegmentRuleDtoList(wrapper);
    }

    /**
     * V3版本：根据定价配置ID查询重量区间规则列表
     * <pre>
     * 该方法为V3数据模型新增，实现按定价配置ID查询重量区间规则列表的功能。
     * 在V3模型中，重量规则直接关联到定价配置，这样可以实现更精细的规则管理。
     *
     * @param pricingId 定价配置ID
     * @return 符合条件的重量区间规则列表
     */
    @Override
    public List<WeightSegmentRuleVO> listByPricingId(Long pricingId) {
        LambdaQueryWrapper<WeightSegmentRuleDO> wrapper = Wrappers.<WeightSegmentRuleDO>lambdaQuery()
                .eq(WeightSegmentRuleDO::getPricingId, pricingId);
        // 按重量上限升序排列，便于运费计算时匹配
        wrapper.orderByAsc(WeightSegmentRuleDO::getUpperBound);

        return getWeightSegmentRuleDtoList(wrapper);
    }

    /**
     * 获取权重段规则 dto 列表
     *
     * @param wrapper 查询条件
     * @return 列表
     */
    private List<WeightSegmentRuleVO> getWeightSegmentRuleDtoList(LambdaQueryWrapper<WeightSegmentRuleDO> wrapper) {
        List<WeightSegmentRuleDO> list = this.list(wrapper);
        return list.stream().map(entity -> {
            WeightSegmentRuleVO dto = new WeightSegmentRuleVO();
            BeanUtils.copyProperties(entity, dto);
            return dto;
        }).collect(Collectors.toList());
    }

    /**
     * 根据策略ID批量删除重量区间规则
     * <pre>
     * 该方法实现按策略ID批量删除该策略下所有重量区间规则的功能。
     * 主要用于当策略的计费模式从"首重续重+重量区间"改为"首重续重"时，
     * 需要清除该策略下的所有重量区间规则，因为纯首重续重模式不需要重量区间。
     *
     * @param strategyId 策略ID
     * @return 删除的数量
     */
    @Override
    public R<Integer> deleteByStrategyId(Long strategyId) {
        if (strategyId == null) {
            return R.failed("策略ID不能为空");
        }

        log.info("正在清除策略ID={}下的所有重量区间规则", strategyId);

        // 使用Mapper中的批量删除方法
        Integer deletedCount = this.baseMapper.deleteByStrategyId(strategyId);

        log.info("成功清除策略ID={}下的{}个重量区间规则", strategyId, deletedCount);
        return R.success("清除成功", deletedCount);
    }
}
